package com.cash.app.utils.extra.tools

import android.annotation.SuppressLint
import android.app.ActivityManager
import android.app.usage.StorageStatsManager
import android.content.Context
import android.os.Build
import android.os.Environment
import android.os.StatFs
import android.os.storage.StorageManager
import android.os.storage.StorageVolume
import android.util.Log
import androidx.annotation.RequiresApi
import com.cash.app.utils.extra.ExtraInfoUtils
import com.cash.app.utils.extra.model.StorageData
import java.io.File
import java.io.IOException
import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Method
import java.util.Locale
import java.util.UUID

object StorageQueryUtil {
    private const val TAG = "storage"

    @RequiresApi(Build.VERSION_CODES.O)
    @SuppressLint("SoonBlockedPrivateApi")
    fun queryWithStorageManager(storageData: StorageData): StorageData {
        val storageManager =
            ExtraInfoUtils.getApp().getSystemService(Context.STORAGE_SERVICE) as StorageManager
        val unit = 1024f
        val unit2 = 1000f
        val version = Build.VERSION.SDK_INT
        if (version < Build.VERSION_CODES.M) {
            try {
                val getVolumeList = StorageManager::class.java.getDeclaredMethod("getVolumeList")
                val volumeList: Array<StorageVolume> =
                    getVolumeList.invoke(storageManager) as Array<StorageVolume>
                var totalSize: Long = 0
                var availableSize: Long = 0
                if (volumeList != null) {
                    var getPathFile: Method? = null
                    for (volume in volumeList) {
                        if (getPathFile == null) {
                            getPathFile = volume.javaClass.getDeclaredMethod("getPathFile")
                        }
                        val file = getPathFile?.invoke(volume) as File
                        totalSize += file.totalSpace
                        availableSize += file.usableSpace
                    }
                }
                Log.d(
                    TAG, "totalSize = " + getUnit(
                        totalSize.toFloat(), unit
                    ) + " ,availableSize = " + getUnit(availableSize.toFloat(), unit)
                )
            } catch (e: NoSuchMethodException) {
                e.printStackTrace()
            } catch (e: IllegalAccessException) {
                e.printStackTrace()
            } catch (e: InvocationTargetException) {
                e.printStackTrace()
            }
        } else {
            try {
                val getVolumes = StorageManager::class.java.getDeclaredMethod("getVolumes") //6.0
                val getVolumeInfo = getVolumes.invoke(storageManager) as List<Any>
                var total = 0L
                var used = 0L
                var systemSize = 0L
                for (obj in getVolumeInfo) {
                    val getType = obj.javaClass.getField("type")
                    val type = getType.getInt(obj)
                    Log.d(TAG, "type: $type")
                    if (type == 1) { //TYPE_PRIVATE
                        var totalSize = 0L
                        if (version >= Build.VERSION_CODES.O) { //8.0
                            val getFsUuid = obj.javaClass.getDeclaredMethod("getFsUuid")
                            val fsUuid = getFsUuid.invoke(obj) as String
                            totalSize = getTotalSize(fsUuid) //8.0
                        } else if (version >= Build.VERSION_CODES.N_MR1) { //7.1.1
                            val getPrimaryStorageSize =
                                StorageManager::class.java.getMethod("getPrimaryStorageSize") //5.0 6.0 7.0没有
                            totalSize = getPrimaryStorageSize.invoke(storageManager) as Long
                        }
                        val isMountedReadable = obj.javaClass.getDeclaredMethod("isMountedReadable")
                        val readable = isMountedReadable.invoke(obj) as Boolean
                        if (readable) {
                            val file = obj.javaClass.getDeclaredMethod("getPath")
                            val f = file.invoke(obj) as File
                            if (totalSize == 0L) {
                                totalSize = f.totalSpace
                            }
                            systemSize = totalSize - f.totalSpace
                            used += totalSize - f.freeSpace
                            total += totalSize
                        }
                        Log.d(
                            TAG, ("type = " + type + "totalSize = " + getUnit(
                                totalSize.toFloat(), unit
                            ) + " ,used(with system) = " + getUnit(
                                used.toFloat(), unit
                            ) + " ,free = " + getUnit((totalSize - used).toFloat(), unit))
                        )
                    } else if (type == 0) { //TYPE_PUBLIC
                        //外置存储
                        val isMountedReadable = obj.javaClass.getDeclaredMethod("isMountedReadable")
                        val readable = isMountedReadable.invoke(obj) as Boolean
                        if (readable) {
                            val file = obj.javaClass.getDeclaredMethod("getPath")
                            val f = file.invoke(obj) as File
                            used += f.totalSpace - f.freeSpace
                            total += f.totalSpace
                        }
                    } else if (type == 2) { //TYPE_EMULATED
                    }
                }
                Log.d(
                    TAG, ("""
     total = ${
                        getUnit(
                            total.toFloat(), unit
                        )
                    }
     used(with system) = ${
                        getUnit(
                            used.toFloat(), unit
                        )
                    } available = ${
                        getUnit(
                            (total - used).toFloat(), unit
                        )
                    } systemSize：${getUnit(systemSize.toFloat(), unit)}
     """.trimIndent())
                )

                Log.d(
                    TAG, ("""
     total = ${
                        getUnit(
                            total.toFloat(), unit2
                        )
                    }
     used(with system) = ${
                        getUnit(
                            used.toFloat(), 1000f
                        )
                    } available = ${
                        getUnit(
                            (total - used).toFloat(), unit2
                        )
                    }systemSize：${getUnit(systemSize.toFloat(), unit2)}
     """.trimIndent())
                )
                storageData.memory_card_size = total
                storageData.memory_card_size_use = used
            } catch (e: SecurityException) {
                Log.e(TAG, "permission.PACKAGE_USAGE_STATS")
            } catch (e: Exception) {
                e.printStackTrace()
                queryWithStatFs(storageData)
            }
        }
        return storageData
    }

    fun queryWithStatFs(storageData: StorageData) {
        val statFs: StatFs = StatFs(Environment.getExternalStorageDirectory().path)

        val blockCount: Long = statFs.getBlockCount().toLong()
        val blockSize: Long = statFs.getBlockSize().toLong()
        val availableCount: Long = statFs.getAvailableBlocks().toLong()
        val freeBlocks: Long = statFs.getFreeBlocks().toLong()

        //level 18
//        long totalSize = statFs.getTotalBytes();
//        long availableSize = statFs.getAvailableBytes();
        Log.d(TAG, "=========")
        Log.d(TAG, "total = " + getUnit((blockSize * blockCount).toFloat(), 1024f))
        Log.d(TAG, "available = " + getUnit((blockSize * availableCount).toFloat(), 1024f))
        Log.d(TAG, "free = " + getUnit((blockSize * freeBlocks).toFloat(), 1024f))

        storageData.memory_card_size = blockSize * blockCount
        storageData.memory_card_size_use = blockSize * availableCount
    }

    private val units = arrayOf("B", "KB", "MB", "GB", "TB")

    fun getUnit(size: Float, base: Float): String {
        val index = 0
        //        while (size > base && index < 4) {
//            size = size / base;
//            index++;
//        }
        return String.format(
            Locale.getDefault(), " %.2f %s ", size, units[index]
        )
    }

    /**
     * API 26 android O
     */
    @RequiresApi(Build.VERSION_CODES.O)
    fun getTotalSize(fsUuid: String?): Long {
        try {
            val id = if (fsUuid == null) {
                StorageManager.UUID_DEFAULT
            } else {
                UUID.fromString(fsUuid)
            }
            val stats: StorageStatsManager =
                ExtraInfoUtils.getApp().getSystemService(StorageStatsManager::class.java)
            return stats.getTotalBytes(id)
        } catch (e: NoSuchFieldError) {
            e.printStackTrace()
            return -1
        } catch (e: NoClassDefFoundError) {
            e.printStackTrace()
            return -1
        } catch (e: NullPointerException) {
            e.printStackTrace()
            return -1
        } catch (e: IOException) {
            e.printStackTrace()
            return -1
        }
    }


    val phoneSize: Long
        get() {
            val actManager: ActivityManager = ExtraInfoUtils.getApp()
                .getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
            val memInfo: ActivityManager.MemoryInfo = ActivityManager.MemoryInfo()
            actManager.getMemoryInfo(memInfo)
            val totalMemory: Long = memInfo.totalMem
            return totalMemory
        }
}
